package com.example.infrastructure.aspect;

import com.example.infrastructure.annotation.Idempotent;
import com.example.infrastructure.exception.BusinessException;
import com.example.infrastructure.security.JwtUtils;
import com.example.infrastructure.utils.IpUtils;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

@Slf4j
@Aspect
@Component
public class IdempotentAspect {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private JwtUtils jwtUtils;
    
    private final ExpressionParser parser = new SpelExpressionParser();

    @Around("@annotation(idempotent)")
    public Object around(ProceedingJoinPoint joinPoint, Idempotent idempotent) throws Throwable {
        String key = generateKey(joinPoint, idempotent);
        
        // 尝试获取锁
        Boolean lockAcquired = redisTemplate.opsForValue().setIfAbsent(
            key, "1", idempotent.expireTime(), idempotent.timeUnit());
        
        if (!Boolean.TRUE.equals(lockAcquired)) {
            throw new BusinessException(429, idempotent.message());
        }
        
        try {
            return joinPoint.proceed();
        } finally {
            // 方法执行完成后不立即删除key，让其自然过期，防止重复提交
        }
    }
    
    private String generateKey(ProceedingJoinPoint joinPoint, Idempotent idempotent) {
        StringBuilder keyBuilder = new StringBuilder(idempotent.prefix()).append(":");
        
        switch (idempotent.type()) {
            case USER_ID:
                Long userId = getCurrentUserId();
                keyBuilder.append("user:").append(userId != null ? userId : "anonymous");
                break;
                
            case IP:
                String ip = getCurrentIp();
                keyBuilder.append("ip:").append(ip != null ? ip : "unknown");
                break;
                
            case CUSTOM:
                if (StringUtils.hasText(idempotent.key())) {
                    String customKey = parseSpEL(idempotent.key(), joinPoint);
                    keyBuilder.append("custom:").append(customKey);
                } else {
                    keyBuilder.append("default");
                }
                break;
        }
        
        // 添加方法签名
        keyBuilder.append(":").append(joinPoint.getSignature().toShortString());
        
        return keyBuilder.toString();
    }
    
    private Long getCurrentUserId() {
        try {
            HttpServletRequest request = getHttpServletRequest();
            if (request != null) {
                String token = getTokenFromRequest(request);
                if (StringUtils.hasText(token)) {
                    return jwtUtils.getUserIdFromToken(token);
                }
            }
        } catch (Exception e) {
            log.warn("获取当前用户ID失败: {}", e.getMessage());
        }
        return null;
    }
    
    private String getCurrentIp() {
        try {
            HttpServletRequest request = getHttpServletRequest();
            if (request != null) {
                return IpUtils.getClientIp(request);
            }
        } catch (Exception e) {
            log.warn("获取当前IP失败: {}", e.getMessage());
        }
        return null;
    }
    
    private String parseSpEL(String spel, ProceedingJoinPoint joinPoint) {
        try {
            Expression expression = parser.parseExpression(spel);
            EvaluationContext context = new StandardEvaluationContext();
            
            // 设置方法参数
            Object[] args = joinPoint.getArgs();
            for (int i = 0; i < args.length; i++) {
                context.setVariable("arg" + i, args[i]);
            }
            
            return expression.getValue(context, String.class);
        } catch (Exception e) {
            log.error("解析SpEL表达式失败: {}", spel, e);
            return "error";
        }
    }
    
    private HttpServletRequest getHttpServletRequest() {
        ServletRequestAttributes attributes = 
            (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return attributes != null ? attributes.getRequest() : null;
    }
    
    private String getTokenFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }
}